// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h"

#include <gestures/gestures.h>
#include <libevdev/libevdev.h>

#include <algorithm>
#include <fnmatch.h>
#include <stdint.h>
#include <string.h>

#include "base/containers/hash_tables.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/strings/stringize_macros.h"
#include "base/strings/stringprintf.h"
#include "ui/events/ozone/evdev/libgestures_glue/gesture_feedback.h"
#include "ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.h"

// GesturesProp implementation.
//
// Check the header file for its definition.
GesturesProp::GesturesProp(const std::string& name,
    const PropertyType type,
    const size_t count)
    : name_(name)
    , type_(type)
    , count_(count)
    , get_(NULL)
    , set_(NULL)
    , handler_data_(NULL)
{
}

std::vector<int> GesturesProp::GetIntValue() const
{
    NOTREACHED();
    return std::vector<int>();
}

bool GesturesProp::SetIntValue(const std::vector<int>& value)
{
    NOTREACHED();
    return false;
}

std::vector<int16_t> GesturesProp::GetShortValue() const
{
    NOTREACHED();
    return std::vector<int16_t>();
}

bool GesturesProp::SetShortValue(const std::vector<int16_t>& value)
{
    NOTREACHED();
    return false;
}

std::vector<bool> GesturesProp::GetBoolValue() const
{
    NOTREACHED();
    return std::vector<bool>();
}

bool GesturesProp::SetBoolValue(const std::vector<bool>& value)
{
    NOTREACHED();
    return false;
}

std::string GesturesProp::GetStringValue() const
{
    NOTREACHED();
    return std::string();
}

bool GesturesProp::SetStringValue(const std::string& value)
{
    NOTREACHED();
    return false;
}

std::vector<double> GesturesProp::GetDoubleValue() const
{
    NOTREACHED();
    return std::vector<double>();
}

bool GesturesProp::SetDoubleValue(const std::vector<double>& value)
{
    NOTREACHED();
    return false;
}

void GesturesProp::SetHandlers(GesturesPropGetHandler get,
    GesturesPropSetHandler set,
    void* data)
{
    get_ = get;
    set_ = set;
    handler_data_ = data;
}

void GesturesProp::OnGet() const
{
    // We don't have the X server now so there is currently nothing to do when
    // the get handler returns true.
    // TODO(sheckylin): Re-visit this if we use handlers that modifies the
    // property.
    if (get_)
        get_(handler_data_);
}

void GesturesProp::OnSet() const
{
    // Call the property set handler if available.
    if (set_)
        set_(handler_data_);
}

const char** GesturesProp::GetStringWritebackPtr() const
{
    NOTREACHED();
    return NULL;
}

bool GesturesProp::IsAllocated() const
{
    NOTREACHED();
    return false;
}

// Type-templated GesturesProp.
template <typename T>
class TypedGesturesProp : public GesturesProp {
public:
    TypedGesturesProp(const std::string& name,
        const PropertyType type,
        const size_t count,
        T* value)
        : GesturesProp(name, type, count)
        , value_(value)
        , is_read_only_(false)
        , is_allocated_(false)
    {
        Init();
    }
    ~TypedGesturesProp() override
    {
        if (is_allocated_)
            delete[] value_;
    }

    // Accessors.
    bool IsReadOnly() const override { return is_read_only_; }

protected:
    // Functions for setting/getting numerical properties.
    //
    // These two functions calls the set/get handler and should only be used in
    // Get*Value/Set*Value functions.
    template <typename U>
    std::vector<U> GetNumericalPropertyValue() const
    {
        // Nothing should be modified so it is OK to call the get handler first.
        OnGet();
        return this->template GetNumericalValue<U>();
    }

    template <typename U>
    bool SetNumericalPropertyValue(const std::vector<U>& value)
    {
        // Set the value only if not read-only and the vector size matches.
        //
        // As per the legacy guideline, all read-only properties (created with NULL)
        // can't be modified. If we want to change this in the future, re-think
        // about the different cases here (e.g., should we allow setting an array
        // value of different size?).
        if (is_read_only_ || value.size() != count())
            return false;
        bool ret = this->template SetNumericalValue(value);
        OnSet();
        return ret;
    }

    // Initialize a numerical property's value. Note that a (numerical) default
    // property's value is always stored in double.
    void InitializeNumericalProperty(const T* init,
        const GesturesProp* default_property)
    {
        if (IsDefaultPropertyUsable(default_property)) {
            DVLOG(2) << "Default property found. Using its value ...";
            this->template SetNumericalValue(default_property->GetDoubleValue());
        } else {
            // To work with the interface exposed by the gesture lib, we have no
            // choice but to trust that the init array has sufficient size.
            std::vector<T> temp(init, init + count());
            this->template SetNumericalValue(temp);
        }
    }

    // Data pointer.
    T* value_;

    // If the flag is on, it means the GesturesProp is created by passing a NULL
    // data pointer to the creator functions. We define the property as a
    // read-only one and that no value change will be allowed for it. Note that
    // the flag is different from is_allocated in that StringProperty will always
    // allocate no matter it is created with NULL or not.
    bool is_read_only_;

private:
    // Initialize the object.
    void Init()
    {
        // If no external data pointer is passed, we have to create our own.
        if (!value_) {
            value_ = new T[GesturesProp::count()];
            is_read_only_ = true;
            is_allocated_ = true;
        }
    }

    // Low-level functions for setting/getting numerical properties.
    template <typename U>
    std::vector<U> GetNumericalValue() const
    {
        // We do type-casting because the numerical types may not totally match.
        // For example, we store bool as GesturesPropBool to be compatible with the
        // gesture library. Also, all parsed xorg-conf property values are stored
        // as double because we can't identify their original type lexically.
        // TODO(sheckylin): Handle value out-of-range (e.g., double to int).
        std::vector<U> result(count());
        for (size_t i = 0; i < count(); ++i)
            result[i] = static_cast<U>(value_[i]);
        return result;
    }

    template <typename U>
    bool SetNumericalValue(const std::vector<U>& value)
    {
        for (size_t i = 0; i < count(); ++i)
            value_[i] = static_cast<T>(value[i]);
        return true;
    }

    // Check if a default property usable for (numerical) initialization.
    bool IsDefaultPropertyUsable(const GesturesProp* default_property) const
    {
        // We currently assumed that we won't specify any array property in the
        // configuration files. The code needs to be updated if the assumption
        // becomes invalid in the future.
        return (count() == 1 && default_property && default_property->type() != PropertyType::PT_STRING);
    }

    // Accessors.
    bool IsAllocated() const override { return is_allocated_; }

    // If the flag is on, it means the memory that the data pointer points to is
    // allocated here. We will need to free the memory by ourselves when the
    // GesturesProp is destroyed.
    bool is_allocated_;
};

class GesturesIntProp : public TypedGesturesProp<int> {
public:
    GesturesIntProp(const std::string& name,
        const size_t count,
        int* value,
        const int* init,
        const GesturesProp* default_property)
        : TypedGesturesProp<int>(name, PropertyType::PT_INT, count, value)
    {
        InitializeNumericalProperty(init, default_property);
    }
    std::vector<int> GetIntValue() const override
    {
        return this->template GetNumericalPropertyValue<int>();
    }
    bool SetIntValue(const std::vector<int>& value) override
    {
        return this->template SetNumericalPropertyValue(value);
    }
};

class GesturesShortProp : public TypedGesturesProp<short> {
public:
    GesturesShortProp(const std::string& name,
        const size_t count,
        short* value,
        const short* init,
        const GesturesProp* default_property)
        : TypedGesturesProp<short>(name, PropertyType::PT_SHORT, count, value)
    {
        InitializeNumericalProperty(init, default_property);
    }
    std::vector<int16_t> GetShortValue() const override
    {
        return this->template GetNumericalPropertyValue<int16_t>();
    }
    bool SetShortValue(const std::vector<int16_t>& value) override
    {
        return this->template SetNumericalPropertyValue(value);
    }
};

class GesturesBoolProp : public TypedGesturesProp<GesturesPropBool> {
public:
    GesturesBoolProp(const std::string& name,
        const size_t count,
        GesturesPropBool* value,
        const GesturesPropBool* init,
        const GesturesProp* default_property)
        : TypedGesturesProp<GesturesPropBool>(name,
            PropertyType::PT_BOOL,
            count,
            value)
    {
        InitializeNumericalProperty(init, default_property);
    }
    std::vector<bool> GetBoolValue() const override
    {
        return this->template GetNumericalPropertyValue<bool>();
    }
    bool SetBoolValue(const std::vector<bool>& value) override
    {
        return this->template SetNumericalPropertyValue(value);
    }
};

class GesturesDoubleProp : public TypedGesturesProp<double> {
public:
    GesturesDoubleProp(const std::string& name,
        const size_t count,
        double* value,
        const double* init,
        const GesturesProp* default_property)
        : TypedGesturesProp<double>(name, PropertyType::PT_REAL, count, value)
    {
        InitializeNumericalProperty(init, default_property);
    }
    std::vector<double> GetDoubleValue() const override
    {
        return this->template GetNumericalPropertyValue<double>();
    }
    bool SetDoubleValue(const std::vector<double>& value) override
    {
        return this->template SetNumericalPropertyValue(value);
    }
};

class GesturesStringProp : public TypedGesturesProp<std::string> {
public:
    // StringProperty's memory is always allocated on this side instead of
    // externally in the gesture lib as the original one will be destroyed right
    // after the constructor call (check the design of StringProperty). To do
    // this, we call the TypedGesturesProp constructor with NULL pointer so that
    // it always allocates.
    GesturesStringProp(const std::string& name,
        const char** value,
        const char* init,
        const GesturesProp* default_property)
        : TypedGesturesProp<std::string>(name, PropertyType::PT_STRING, 1, NULL)
        , write_back_(NULL)
    {
        InitializeStringProperty(value, init, default_property);
    }
    std::string GetStringValue() const override
    {
        OnGet();
        return *value_;
    }
    bool SetStringValue(const std::string& value) override
    {
        if (is_read_only_)
            return false;
        *value_ = value;

        // Write back the pointer in case it may change (e.g., string
        // re-allocation).
        if (write_back_)
            *(write_back_) = value_->c_str();
        OnSet();
        return true;
    }

private:
    // Initialize the object.
    void InitializeStringProperty(const char** value,
        const char* init,
        const GesturesProp* default_property)
    {
        // Initialize the property value similar to the numerical types.
        if (IsDefaultPropertyUsable(default_property)) {
            DVLOG(2) << "Default property found. Using its value ...";
            *value_ = default_property->GetStringValue();
        } else {
            *value_ = init;
        }

        // If the provided pointer is not NULL, replace its content
        // (val_ of StringProperty) with the address of our allocated string.
        // Note that we don't have to do this for the other data types as they will
        // use the original data pointer if possible and it is unnecessary to do so
        // if the pointer is NULL.
        if (value) {
            *value = value_->c_str();
            write_back_ = value;
            // Set the read-only flag back to false.
            is_read_only_ = false;
        }
    }

    // Re-write the function with different criteria as we want string properties
    // now.
    bool IsDefaultPropertyUsable(const GesturesProp* default_property) const
    {
        return (default_property && default_property->type() == PropertyType::PT_STRING);
    }

    const char** GetStringWritebackPtr() const override { return write_back_; }

    // In some cases, we don't directly use the data pointer provided by the
    // creators due to its limitation and instead use our own types (e.g., in
    // the case of string). We thus need to store the write back pointer so that
    // we can update the value in the gesture lib if the property value gets
    // changed.
    const char** write_back_;
};

// Anonymous namespace for utility functions and internal constants.
namespace {

// The path that we will look for conf files.
const char kConfigurationFilePath[] = "/etc/gesture";

// We support only match types that have already been used. One should change
// this if we start using new types in the future. Note that most unsupported
// match types are either useless in CrOS or inapplicable to the non-X
// environment.
const char* kSupportedMatchTypes[] = { "MatchProduct",
    "MatchDevicePath",
    "MatchUSBID",
    "MatchIsPointer",
    "MatchIsTouchpad",
    "MatchIsTouchscreen" };
const char* kUnsupportedMatchTypes[] = { "MatchVendor",
    "MatchOS",
    "MatchPnPID",
    "MatchDriver",
    "MatchTag",
    "MatchLayout",
    "MatchIsKeyboard",
    "MatchIsJoystick",
    "MatchIsTablet" };

// Special keywords for boolean values.
const char* kTrue[] = { "on", "true", "yes" };
const char* kFalse[] = { "off", "false", "no" };

// Check if a device falls into one device type category.
bool IsDeviceOfType(const ui::GesturePropertyProvider::DevicePtr device,
    const ui::EventDeviceType type,
    const GesturesProp* device_mouse_property,
    const GesturesProp* device_touchpad_property)
{
    // Get the device type info from gesture properties if they are available.
    // Otherwise, fallback to the libevdev device info.
    bool is_mouse = false, is_touchpad = false;
    EvdevClass evdev_class = device->info.evdev_class;
    if (device_mouse_property) {
        is_mouse = device_mouse_property->GetBoolValue()[0];
    } else {
        is_mouse = (evdev_class == EvdevClassMouse || evdev_class == EvdevClassMultitouchMouse);
    }
    if (device_touchpad_property) {
        is_touchpad = device_touchpad_property->GetBoolValue()[0];
    } else {
        is_touchpad = (evdev_class == EvdevClassTouchpad || evdev_class == EvdevClassTouchscreen || evdev_class == EvdevClassMultitouchMouse);
    }

    switch (type) {
    case ui::DT_KEYBOARD:
        return (evdev_class == EvdevClassKeyboard);
        break;
    case ui::DT_MOUSE:
        return is_mouse;
        break;
    case ui::DT_TOUCHPAD:
        return (!is_mouse) && is_touchpad;
        break;
    case ui::DT_TOUCHSCREEN:
        return (evdev_class == EvdevClassTouchscreen);
        break;
    case ui::DT_MULTITOUCH:
        return is_touchpad;
        break;
    case ui::DT_MULTITOUCH_MOUSE:
        return is_mouse && is_touchpad;
        break;
    case ui::DT_ALL:
        return true;
        break;
    default:
        break;
    }
    return false;
}

// Trick to get the device path from a file descriptor.
std::string GetDeviceNodePath(
    const ui::GesturePropertyProvider::DevicePtr device)
{
    std::string proc_symlink = "/proc/self/fd/" + base::IntToString(device->fd);
    base::FilePath path;
    if (!base::ReadSymbolicLink(base::FilePath(proc_symlink), &path))
        return std::string();
    return path.value();
}

// Check if a match criteria is currently implemented. Note that we didn't
// implemented all of them as some are inapplicable in the non-X world.
bool IsMatchTypeSupported(const std::string& match_type)
{
    for (size_t i = 0; i < arraysize(kSupportedMatchTypes); ++i)
        if (match_type == kSupportedMatchTypes[i])
            return true;
    for (size_t i = 0; i < arraysize(kUnsupportedMatchTypes); ++i) {
        if (match_type == kUnsupportedMatchTypes[i]) {
            LOG(ERROR) << "Unsupported gestures input class match type: "
                       << match_type;
            return false;
        }
    }
    return false;
}

// Check if a match criteria is a device type one.
bool IsMatchDeviceType(const std::string& match_type)
{
    return base::StartsWith(match_type, "MatchIs", base::CompareCase::SENSITIVE);
}

// Parse a boolean value keyword (e.g., on/off, true/false).
int ParseBooleanKeyword(const std::string& value)
{
    for (size_t i = 0; i < arraysize(kTrue); ++i) {
        if (base::LowerCaseEqualsASCII(value, kTrue[i]))
            return 1;
    }
    for (size_t i = 0; i < arraysize(kFalse); ++i) {
        if (base::LowerCaseEqualsASCII(value, kFalse[i]))
            return -1;
    }
    return 0;
}

// Log the value of an array property.
template <typename T>
void LogArrayProperty(std::ostream& os, const std::vector<T>& value)
{
    os << "(";
    for (size_t i = 0; i < value.size(); ++i) {
        if (i > 0)
            os << ", ";
        os << value[i];
    }
    os << ")";
}

// Property type logging function.
std::ostream& operator<<(std::ostream& out,
    const ui::GesturePropertyProvider::PropertyType type)
{
    std::string s;
#define TYPE_CASE(TYPE)                       \
    case (ui::GesturePropertyProvider::TYPE): \
        s = #TYPE;                            \
        break;
    switch (type) {
        TYPE_CASE(PT_INT);
        TYPE_CASE(PT_SHORT);
        TYPE_CASE(PT_BOOL);
        TYPE_CASE(PT_STRING);
        TYPE_CASE(PT_REAL);
    default:
        NOTREACHED();
        break;
    }
#undef TYPE_CASE
    return out << s;
}

// A relay function that dumps evdev log to a place that we have access to
// (the default directory is inaccessible without X11).
void DumpTouchEvdevDebugLog(void* data)
{
    Event_Dump_Debug_Log_To(data, ui::kTouchpadEvdevLogPath);
}

} // namespace

// GesturesProp logging function.
std::ostream& operator<<(std::ostream& os, const GesturesProp& prop)
{
    const GesturesProp* property = &prop;

    // Output the property content.
    os << "\"" << property->name() << "\", " << property->type() << ", "
       << property->count() << ", (" << property->IsAllocated() << ", "
       << property->IsReadOnly() << "), ";

    // Only the string property has the write back pointer.
    if (property->type() == ui::GesturePropertyProvider::PT_STRING)
        os << property->GetStringWritebackPtr();
    else
        os << "NULL";

    // Output the property values.
    os << ", ";
    switch (property->type()) {
    case ui::GesturePropertyProvider::PT_INT:
        LogArrayProperty(os, property->GetIntValue());
        break;
    case ui::GesturePropertyProvider::PT_SHORT:
        LogArrayProperty(os, property->GetShortValue());
        break;
    case ui::GesturePropertyProvider::PT_BOOL:
        LogArrayProperty(os, property->GetBoolValue());
        break;
    case ui::GesturePropertyProvider::PT_STRING:
        os << "\"" << property->GetStringValue() << "\"";
        break;
    case ui::GesturePropertyProvider::PT_REAL:
        LogArrayProperty(os, property->GetDoubleValue());
        break;
    default:
        LOG(ERROR) << "Unknown gesture property type: " << property->type();
        NOTREACHED();
        break;
    }
    return os;
}

namespace ui {
namespace internal {

    // Mapping table from a property name to its corresponding GesturesProp
    // object pointer.
    typedef base::hash_map<std::string, GesturesProp*> PropertiesMap;
    typedef base::ScopedPtrHashMap<std::string, scoped_ptr<GesturesProp>>
        ScopedPropertiesMap;

    // Struct holding properties of a device.
    //
    // Note that we can't define it in GesturePropertyProvider as a nested class
    // because ScopedPtrHashMap will require us to expose the GestureProp's
    // destructor so that it can instantiate the object. This is something we
    // don't want to do.
    struct GestureDevicePropertyData {
        GestureDevicePropertyData() { }

        // Properties owned and being used by the device.
        ScopedPropertiesMap properties;

        // Unowned default properties (owned by the configuration file). Their values
        // will be applied when a property of the same name is created. These are
        // usually only a small portion of all properties in use.
        PropertiesMap default_properties;
    };

    // Base class for device match criterias in conf files.
    // Check the xorg-conf spec for more detailed information.
    class MatchCriteria {
    public:
        typedef ui::GesturePropertyProvider::DevicePtr DevicePtr;
        explicit MatchCriteria(const std::string& arg);
        virtual ~MatchCriteria() { }
        virtual bool Match(const DevicePtr device) = 0;

    protected:
        std::vector<std::string> args_;
    };

    // Match a device based on its evdev name string.
    class MatchProduct : public MatchCriteria {
    public:
        explicit MatchProduct(const std::string& arg);
        ~MatchProduct() override { }
        bool Match(const DevicePtr device) override;
    };

    // Math a device based on its device node path.
    class MatchDevicePath : public MatchCriteria {
    public:
        explicit MatchDevicePath(const std::string& arg);
        ~MatchDevicePath() override { }
        bool Match(const DevicePtr device) override;
    };

    // Math a USB device based on its USB vid and pid.
    // Mostly used for external mice and touchpads.
    class MatchUSBID : public MatchCriteria {
    public:
        explicit MatchUSBID(const std::string& arg);
        ~MatchUSBID() override { }
        bool Match(const DevicePtr device) override;

    private:
        bool IsValidPattern(const std::string& pattern);
        std::vector<std::string> vid_patterns_;
        std::vector<std::string> pid_patterns_;
    };

    // Generic base class for device type math criteria.
    class MatchDeviceType : public MatchCriteria {
    public:
        explicit MatchDeviceType(const std::string& arg);
        ~MatchDeviceType() override { }

    protected:
        bool value_;
        bool is_valid_;
    };

    // Check if a device is a pointer device.
    class MatchIsPointer : public MatchDeviceType {
    public:
        explicit MatchIsPointer(const std::string& arg);
        ~MatchIsPointer() override { }
        bool Match(const DevicePtr device) override;
    };

    // Check if a device is a touchpad.
    class MatchIsTouchpad : public MatchDeviceType {
    public:
        explicit MatchIsTouchpad(const std::string& arg);
        ~MatchIsTouchpad() override { }
        bool Match(const DevicePtr device) override;
    };

    // Check if a device is a touchscreen.
    class MatchIsTouchscreen : public MatchDeviceType {
    public:
        explicit MatchIsTouchscreen(const std::string& arg);
        ~MatchIsTouchscreen() override { }
        bool Match(const DevicePtr device) override;
    };

    // Struct for sections in xorg conf files.
    struct ConfigurationSection {
        typedef ui::GesturePropertyProvider::DevicePtr DevicePtr;
        ConfigurationSection() { }
        bool Match(const DevicePtr device);
        std::string identifier;
        std::vector<scoped_ptr<MatchCriteria>> criterias;
        std::vector<scoped_ptr<GesturesProp>> properties;
    };

    MatchCriteria::MatchCriteria(const std::string& arg)
    {
        // TODO(sheckylin): Should we trim all tokens here?
        args_ = base::SplitString(
            arg, "|", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
        if (args_.empty()) {
            LOG(ERROR) << "Empty match pattern found, will evaluate to the default "
                          "value (true): \""
                       << arg << "\"";
        }
    }

    MatchProduct::MatchProduct(const std::string& arg)
        : MatchCriteria(arg)
    {
    }

    bool MatchProduct::Match(const DevicePtr device)
    {
        if (args_.empty())
            return true;
        std::string name(device->info.name);
        for (size_t i = 0; i < args_.size(); ++i)
            if (name.find(args_[i]) != std::string::npos)
                return true;
        return false;
    }

    MatchDevicePath::MatchDevicePath(const std::string& arg)
        : MatchCriteria(arg)
    {
    }

    bool MatchDevicePath::Match(const DevicePtr device)
    {
        if (args_.empty())
            return true;

        // Check if the device path matches any pattern.
        std::string path = GetDeviceNodePath(device);
        if (path.empty())
            return false;
        for (size_t i = 0; i < args_.size(); ++i)
            if (fnmatch(args_[i].c_str(), path.c_str(), FNM_NOESCAPE) == 0)
                return true;
        return false;
    }

    MatchUSBID::MatchUSBID(const std::string& arg)
        : MatchCriteria(arg)
    {
        // Check each pattern and split valid ones into vids and pids.
        for (size_t i = 0; i < args_.size(); ++i) {
            if (!IsValidPattern(args_[i])) {
                LOG(ERROR) << "Invalid USB ID: " << args_[i];
                continue;
            }
            std::vector<std::string> tokens = base::SplitString(
                args_[i], ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
            vid_patterns_.push_back(base::ToLowerASCII(tokens[0]));
            pid_patterns_.push_back(base::ToLowerASCII(tokens[1]));
        }
        if (vid_patterns_.empty()) {
            LOG(ERROR) << "No valid USB ID pattern found, will be ignored: \"" << arg
                       << "\"";
        }
    }

    bool MatchUSBID::Match(const DevicePtr device)
    {
        if (vid_patterns_.empty())
            return true;
        std::string vid = base::StringPrintf("%04x", device->info.id.vendor);
        std::string pid = base::StringPrintf("%04x", device->info.id.product);
        for (size_t i = 0; i < vid_patterns_.size(); ++i) {
            if (fnmatch(vid_patterns_[i].c_str(), vid.c_str(), FNM_NOESCAPE) == 0 && fnmatch(pid_patterns_[i].c_str(), pid.c_str(), FNM_NOESCAPE) == 0) {
                return true;
            }
        }
        return false;
    }

    bool MatchUSBID::IsValidPattern(const std::string& pattern)
    {
        // Each USB id should be in the lsusb format, i.e., xxxx:xxxx. We choose to do
        // a lazy check here: if the pattern contains wrong characters not in the hex
        // number range, it won't be matched anyway.
        int number_of_colons = 0;
        size_t pos_of_colon = 0;
        for (size_t i = 0; i < pattern.size(); ++i)
            if (pattern[i] == ':')
                ++number_of_colons, pos_of_colon = i;
        return (number_of_colons == 1) && (pos_of_colon != 0) && (pos_of_colon != pattern.size() - 1);
    }

    MatchDeviceType::MatchDeviceType(const std::string& arg)
        : MatchCriteria(arg)
        , value_(true)
        , is_valid_(false)
    {
        // Default value of a match criteria is true.
        if (args_.empty())
            args_.push_back("on");

        // We care only about the first argument.
        int value = ParseBooleanKeyword(args_[0]);
        if (value) {
            is_valid_ = true;
            value_ = value > 0;
        }
        if (!is_valid_) {
            LOG(ERROR)
                << "No valid device class boolean keyword found, will be ignored: \""
                << arg << "\"";
        }
    }

    MatchIsPointer::MatchIsPointer(const std::string& arg)
        : MatchDeviceType(arg)
    {
    }

    bool MatchIsPointer::Match(const DevicePtr device)
    {
        if (!is_valid_)
            return true;
        return (value_ == (device->info.evdev_class == EvdevClassMouse || device->info.evdev_class == EvdevClassMultitouchMouse));
    }

    MatchIsTouchpad::MatchIsTouchpad(const std::string& arg)
        : MatchDeviceType(arg)
    {
    }

    bool MatchIsTouchpad::Match(const DevicePtr device)
    {
        if (!is_valid_)
            return true;
        return (value_ == (device->info.evdev_class == EvdevClassTouchpad));
    }

    MatchIsTouchscreen::MatchIsTouchscreen(const std::string& arg)
        : MatchDeviceType(arg)
    {
    }

    bool MatchIsTouchscreen::Match(const DevicePtr device)
    {
        if (!is_valid_)
            return true;
        return (value_ == (device->info.evdev_class == EvdevClassTouchscreen));
    }

    bool ConfigurationSection::Match(DevicePtr device)
    {
        for (size_t i = 0; i < criterias.size(); ++i)
            if (!criterias[i]->Match(device))
                return false;
        return true;
    }

} // namespace internal

GesturePropertyProvider::GesturePropertyProvider()
{
    LoadDeviceConfigurations();
}

GesturePropertyProvider::~GesturePropertyProvider()
{
}

bool GesturePropertyProvider::GetDeviceIdsByType(
    const EventDeviceType type,
    std::vector<DeviceId>* device_ids)
{
    bool exists = false;
    DeviceMap::const_iterator it = device_map_.begin();
    for (; it != device_map_.end(); ++it) {
        if (IsDeviceIdOfType(it->first, type)) {
            exists = true;
            if (device_ids)
                device_ids->push_back(it->first);
        }
    }
    return exists;
}

bool GesturePropertyProvider::IsDeviceIdOfType(const DeviceId device_id,
    const EventDeviceType type)
{
    DeviceMap::const_iterator it = device_map_.find(device_id);
    if (it == device_map_.end())
        return false;
    return IsDeviceOfType(it->second, type,
        GetProperty(device_id, "Device Mouse"),
        GetProperty(device_id, "Device Touchpad"));
}

GesturesProp* GesturePropertyProvider::GetProperty(const DeviceId device_id,
    const std::string& name)
{
    return FindProperty(device_id, name);
}

std::vector<std::string> GesturePropertyProvider::GetPropertyNamesById(
    const DeviceId device_id)
{
    internal::GestureDevicePropertyData* device_data = device_data_map_.get(device_id);
    if (!device_data)
        return std::vector<std::string>();

    // Dump all property names of the device.
    std::vector<std::string> names;
    for (internal::ScopedPropertiesMap::const_iterator it = device_data->properties.begin();
         it != device_data->properties.end(); ++it)
        names.push_back(it->first);
    return names;
}

std::string GesturePropertyProvider::GetDeviceNameById(
    const DeviceId device_id)
{
    DeviceMap::const_iterator it = device_map_.find(device_id);
    if (it == device_map_.end())
        return std::string();
    return std::string(it->second->info.name);
}

void GesturePropertyProvider::RegisterDevice(const DeviceId id,
    const DevicePtr device)
{
    DeviceMap::const_iterator it = device_map_.find(id);
    if (it != device_map_.end())
        return;

    // Setup data-structures.
    device_map_[id] = device;
    device_data_map_.set(id,
        scoped_ptr<internal::GestureDevicePropertyData>(
            new internal::GestureDevicePropertyData));

    // Gather default property values for the device from the parsed conf files.
    SetupDefaultProperties(id, device);
    return;
}

void GesturePropertyProvider::UnregisterDevice(const DeviceId id)
{
    DeviceMap::const_iterator it = device_map_.find(id);
    if (it == device_map_.end())
        return;
    device_data_map_.erase(id);
    device_map_.erase(it);
}

void GesturePropertyProvider::AddProperty(const DeviceId device_id,
    const std::string& name,
    scoped_ptr<GesturesProp> property)
{
    // The look-up should never fail because ideally a property can only be
    // created with GesturesPropCreate* functions from the gesture lib side.
    // Therefore, we simply return on failure.
    internal::GestureDevicePropertyData* device_data = device_data_map_.get(device_id);
    if (device_data)
        device_data->properties.set(name, std::move(property));
}

void GesturePropertyProvider::DeleteProperty(const DeviceId device_id,
    const std::string& name)
{
    internal::GestureDevicePropertyData* device_data = device_data_map_.get(device_id);
    if (device_data)
        device_data->properties.erase(name);
}

GesturesProp* GesturePropertyProvider::FindProperty(const DeviceId device_id,
    const std::string& name)
{
    internal::GestureDevicePropertyData* device_data = device_data_map_.get(device_id);
    if (!device_data)
        return NULL;
    return device_data->properties.get(name);
}

GesturesProp* GesturePropertyProvider::GetDefaultProperty(
    const DeviceId device_id,
    const std::string& name)
{
    internal::GestureDevicePropertyData* device_data = device_data_map_.get(device_id);
    if (!device_data)
        return NULL;
    internal::PropertiesMap::const_iterator ib = device_data->default_properties.find(name);
    if (ib == device_data->default_properties.end())
        return NULL;
    return ib->second;
}

void GesturePropertyProvider::LoadDeviceConfigurations()
{
    // Enumerate conf files and sort them lexicographically.
    std::set<base::FilePath> files;
    base::FileEnumerator file_enum(base::FilePath(kConfigurationFilePath),
        false,
        base::FileEnumerator::FILES,
        "*.conf");
    for (base::FilePath path = file_enum.Next(); !path.empty();
         path = file_enum.Next()) {
        files.insert(path);
    }
    DVLOG(2) << files.size() << " conf files were found";

    // Parse conf files one-by-one.
    for (std::set<base::FilePath>::iterator file_iter = files.begin();
         file_iter != files.end();
         ++file_iter) {
        DVLOG(2) << "Parsing conf file: " << (*file_iter).value();
        std::string content;
        if (!base::ReadFileToString(*file_iter, &content)) {
            LOG(ERROR) << "Can't loading gestures conf file: "
                       << (*file_iter).value();
            continue;
        }
        ParseXorgConfFile(content);
    }
}

void GesturePropertyProvider::ParseXorgConfFile(const std::string& content)
{
    // To simplify the parsing work, we made some assumption about the conf file
    // format which doesn't exist in the original xorg-conf spec. Most important
    // ones are:
    // 1. All keywords and names are now case-sensitive. Also, underscores are not
    //    ignored.
    // 2. Each entry takes up one and exactly one line in the file.
    // 3. No negation of the option value even if the option name is prefixed with
    //    "No" as it may cause problems for option names that does start with "No"
    //    (e.g., "Non-linearity").

    // Break the content into sections, lines and then pieces.
    // Sections are delimited by the "EndSection" keyword.
    // Lines are delimited by "\n".
    // Pieces are delimited by all white-spaces.
    std::vector<std::string> sections;
    base::SplitStringUsingSubstr(content, "EndSection", &sections);
    for (size_t i = 0; i < sections.size(); ++i) {
        // Create a new configuration section.
        configurations_.push_back(
            make_scoped_ptr(new internal::ConfigurationSection()));
        internal::ConfigurationSection* config = configurations_.back().get();

        // Break the section into lines.
        base::StringTokenizer lines(sections[i], "\n");
        bool is_input_class_section = true;
        bool has_checked_section_type = false;
        while (is_input_class_section && lines.GetNext()) {
            // Parse the line w.r.t. the xorg-conf format.
            std::string line(lines.token());

            // Skip empty lines.
            if (line.empty())
                continue;

            // Treat all whitespaces as delimiters.
            base::StringTokenizer pieces(line, base::kWhitespaceASCII);
            pieces.set_quote_chars("\"");
            bool is_parsing = false;
            bool has_error = false;
            bool next_is_section_type = false;
            bool next_is_option_name = false;
            bool next_is_option_value = false;
            bool next_is_match_criteria = false;
            bool next_is_identifier = false;
            std::string match_type, option_name;
            while (pieces.GetNext()) {
                std::string piece(pieces.token());

                // Skip empty pieces.
                if (piece.empty())
                    continue;

                // See if we are currently parsing an entry or are still looking for
                // one.
                if (is_parsing) {
                    // Stop parsing the current line if the format is wrong.
                    if (piece.size() <= 2 || piece[0] != '\"' || piece[piece.size() - 1] != '\"') {
                        LOG(ERROR) << "Error parsing line: " << lines.token();
                        has_error = true;
                        if (next_is_section_type)
                            is_input_class_section = false;
                        break;
                    }

                    // Parse the arguments. Note that we don't break even if a whitespace
                    // string is passed. It will just be handled in various ways based on
                    // the entry type.
                    std::string arg;
                    base::TrimWhitespaceASCII(
                        piece.substr(1, piece.size() - 2), base::TRIM_ALL, &arg);
                    if (next_is_section_type) {
                        // We only care about InputClass sections.
                        if (arg != "InputClass") {
                            has_error = true;
                            is_input_class_section = false;
                        } else {
                            DVLOG(2) << "New InputClass section found";
                            has_checked_section_type = true;
                        }
                        break;
                    } else if (next_is_identifier) {
                        DVLOG(2) << "Identifier: " << arg;
                        config->identifier = arg;
                        next_is_identifier = false;
                        break;
                    } else if (next_is_option_name) {
                        // TODO(sheckylin): Support option "Ignore".
                        option_name = arg;
                        next_is_option_value = true;
                        next_is_option_name = false;
                    } else if (next_is_option_value) {
                        scoped_ptr<GesturesProp> property = CreateDefaultProperty(option_name, arg);
                        if (property)
                            config->properties.push_back(std::move(property));
                        next_is_option_value = false;
                        break;
                    } else if (next_is_match_criteria) {
                        // Skip all match types that are not supported.
                        if (IsMatchTypeSupported(match_type)) {
                            scoped_ptr<internal::MatchCriteria> criteria = CreateMatchCriteria(match_type, arg);
                            if (criteria)
                                config->criterias.push_back(std::move(criteria));
                        }
                        next_is_match_criteria = false;
                        break;
                    }
                } else {
                    // If the section type hasn't been decided yet, look for it.
                    // Otherwise, look for valid entries according to the spec.
                    if (has_checked_section_type) {
                        if (piece == "Driver") {
                            // "Driver" field is meaningless for non-X11 setup.
                            break;
                        } else if (piece == "Identifier") {
                            is_parsing = true;
                            next_is_identifier = true;
                            continue;
                        } else if (piece == "Option") {
                            is_parsing = true;
                            next_is_option_name = true;
                            continue;
                        } else if (piece.size() > 5 && piece.compare(0, 5, "Match") == 0) {
                            match_type = piece;
                            is_parsing = true;
                            next_is_match_criteria = true;
                            continue;
                        }
                    } else if (piece == "Section") {
                        is_parsing = true;
                        next_is_section_type = true;
                        continue;
                    }

                    // If none of the above is found, check if the current piece starts a
                    // comment.
                    if (piece.empty() || piece[0] != '#') {
                        LOG(ERROR) << "Error parsing line: " << lines.token();
                        has_error = true;
                    }
                    break;
                }
            }

            // The value of a boolean option is skipped (default is true).
            if (!has_error && (next_is_option_value || next_is_match_criteria)) {
                if (next_is_option_value) {
                    scoped_ptr<GesturesProp> property = CreateDefaultProperty(option_name, "on");
                    if (property)
                        config->properties.push_back(std::move(property));
                } else if (IsMatchTypeSupported(match_type) && IsMatchDeviceType(match_type)) {
                    scoped_ptr<internal::MatchCriteria> criteria = CreateMatchCriteria(match_type, "on");
                    if (criteria)
                        config->criterias.push_back(std::move(criteria));
                }
            }
        }

        // Remove useless config sections.
        if (!is_input_class_section || (config->criterias.empty() && config->properties.empty())) {
            configurations_.pop_back();
        }
    }
}

scoped_ptr<internal::MatchCriteria>
GesturePropertyProvider::CreateMatchCriteria(const std::string& match_type,
    const std::string& arg)
{
    DVLOG(2) << "Creating match criteria: (" << match_type << ", " << arg << ")";
    if (match_type == "MatchProduct")
        return make_scoped_ptr(new internal::MatchProduct(arg));
    if (match_type == "MatchDevicePath")
        return make_scoped_ptr(new internal::MatchDevicePath(arg));
    if (match_type == "MatchUSBID")
        return make_scoped_ptr(new internal::MatchUSBID(arg));
    if (match_type == "MatchIsPointer")
        return make_scoped_ptr(new internal::MatchIsPointer(arg));
    if (match_type == "MatchIsTouchpad")
        return make_scoped_ptr(new internal::MatchIsTouchpad(arg));
    if (match_type == "MatchIsTouchscreen")
        return make_scoped_ptr(new internal::MatchIsTouchscreen(arg));
    NOTREACHED();
    return NULL;
}

scoped_ptr<GesturesProp> GesturePropertyProvider::CreateDefaultProperty(
    const std::string& name,
    const std::string& value)
{
    // Our parsing rule:
    // 1. No hex or oct number is accepted.
    // 2. All numbers will be stored as double.
    // 3. Array elements can be separated by both white-spaces or commas.
    // 4. A token is treated as numeric either if it is one of the special
    //    keywords for boolean values (on, true, yes, off, false, no) or if
    //    base::StringToDouble succeeds.
    // 5. The property is treated as numeric if and only if all of its elements
    //    (if any) are numerics. Otherwise, it will be treated as a string.
    // 6. A string property will be trimmed before storing its value.
    DVLOG(2) << "Creating default property: (" << name << ", " << value << ")";

    // Parse elements one-by-one.
    std::string delimiters(base::kWhitespaceASCII);
    delimiters.append(",");
    base::StringTokenizer tokens(value, delimiters);
    bool is_all_numeric = true;
    std::vector<double> numbers;
    while (tokens.GetNext()) {
        // Skip empty tokens.
        std::string token(tokens.token());
        if (token.empty())
            continue;

        // Check if it is a boolean keyword.
        int bool_result = ParseBooleanKeyword(token);
        if (bool_result) {
            numbers.push_back(bool_result > 0);
            continue;
        }

        // Check if it is a number.
        double real_result;
        bool success = base::StringToDouble(token, &real_result);
        if (!success) {
            is_all_numeric = false;
            break;
        }
        numbers.push_back(real_result);
    }

    // Create the GesturesProp. Array properties need to contain at least one
    // number and may contain numbers only.
    scoped_ptr<GesturesProp> property;
    if (is_all_numeric && numbers.size()) {
        property.reset(new GesturesDoubleProp(name, numbers.size(), NULL,
            numbers.data(), NULL));
    } else {
        property.reset(new GesturesStringProp(name, NULL, value.c_str(), NULL));
    }

    DVLOG(2) << "Prop: " << *property;
    // The function will always succeed for now but it may change later if we
    // specify some name or args as invalid.
    return property;
}

void GesturePropertyProvider::SetupDefaultProperties(const DeviceId device_id,
    const DevicePtr device)
{
    DVLOG(2) << "Setting up default properties for (" << device << ", "
             << device_id << ", " << device->info.name << ")";

    // Go through all parsed sections.
    internal::PropertiesMap& property_map = device_data_map_.get(device_id)->default_properties;
    for (const auto& configuration : configurations_) {
        if (configuration->Match(device)) {
            DVLOG(2) << "Conf section \"" << configuration->identifier
                     << "\" is matched";
            for (const auto& property : configuration->properties) {
                // We can't use insert here because a property may be set for several
                // times along the way.
                property_map[property->name()] = property.get();
            }
        }
    }
}

GesturesProp* GesturesPropFunctionsWrapper::CreateInt(void* device_data,
    const char* name,
    int* value,
    size_t count,
    const int* init)
{
    return CreateProperty<int, GesturesIntProp>(
        device_data, name, value, count, init);
}

GesturesProp* GesturesPropFunctionsWrapper::CreateShort(void* device_data,
    const char* name,
    short* value,
    size_t count,
    const short* init)
{
    return CreateProperty<short, GesturesShortProp>(
        device_data, name, value, count, init);
}

GesturesProp* GesturesPropFunctionsWrapper::CreateBool(
    void* device_data,
    const char* name,
    GesturesPropBool* value,
    size_t count,
    const GesturesPropBool* init)
{
    return CreateProperty<GesturesPropBool, GesturesBoolProp>(
        device_data, name, value, count, init);
}

GesturesProp* GesturesPropFunctionsWrapper::CreateReal(void* device_data,
    const char* name,
    double* value,
    size_t count,
    const double* init)
{
    return CreateProperty<double, GesturesDoubleProp>(
        device_data, name, value, count, init);
}

GesturesProp* GesturesPropFunctionsWrapper::CreateString(void* device_data,
    const char* name,
    const char** value,
    const char* init)
{
    GesturesProp* default_property = NULL;
    if (!PreCreateProperty(device_data, name, &default_property))
        return NULL;
    GesturesProp* property = new GesturesStringProp(name, value, init, default_property);
    PostCreateProperty(device_data, name, make_scoped_ptr(property));
    return property;
}

void GesturesPropFunctionsWrapper::RegisterHandlers(
    void* device_data,
    GesturesProp* property,
    void* handler_data,
    GesturesPropGetHandler get,
    GesturesPropSetHandler set)
{
    // Sanity checks
    if (!device_data || !property)
        return;

    property->SetHandlers(get, set, handler_data);
}

void GesturesPropFunctionsWrapper::Free(void* device_data,
    GesturesProp* property)
{
    if (!property)
        return;
    GesturePropertyProvider* provider = GetPropertyProvider(device_data);

    // No need to manually delete the prop pointer as it is implicitly handled
    // with scoped ptr.
    DVLOG(3) << "Freeing Property: \"" << property->name() << "\"";
    provider->DeleteProperty(GetDeviceId(device_data), property->name());
}

bool GesturesPropFunctionsWrapper::InitializeDeviceProperties(
    void* device_data,
    GestureDeviceProperties* properties)
{
    if (!device_data)
        return false;
    GesturePropertyProvider::DevicePtr device = GetDevicePointer(device_data);

    /* Create Device Properties */

    // Read Only properties.
    CreateString(
        device_data, "Device Node", NULL, GetDeviceNodePath(device).c_str());
    short vid = static_cast<short>(device->info.id.vendor);
    CreateShort(device_data, "Device Vendor ID", NULL, 1, &vid);
    short pid = static_cast<short>(device->info.id.product);
    CreateShort(device_data, "Device Product ID", NULL, 1, &pid);

    // Useable trackpad area. If not configured in .conf file,
    // use x/y valuator min/max as reported by kernel driver.
    CreateIntSingle(device_data,
        "Active Area Left",
        &properties->area_left,
        Event_Get_Left(device));
    CreateIntSingle(device_data,
        "Active Area Right",
        &properties->area_right,
        Event_Get_Right(device));
    CreateIntSingle(device_data,
        "Active Area Top",
        &properties->area_top,
        Event_Get_Top(device));
    CreateIntSingle(device_data,
        "Active Area Bottom",
        &properties->area_bottom,
        Event_Get_Bottom(device));

    // Trackpad resolution (pixels/mm). If not configured in .conf file,
    // use x/y resolution as reported by kernel driver.
    CreateIntSingle(device_data,
        "Vertical Resolution",
        &properties->res_y,
        Event_Get_Res_Y(device));
    CreateIntSingle(device_data,
        "Horizontal Resolution",
        &properties->res_x,
        Event_Get_Res_X(device));

    // Trackpad orientation minimum/maximum. If not configured in .conf file,
    // use min/max as reported by kernel driver.
    CreateIntSingle(device_data,
        "Orientation Minimum",
        &properties->orientation_minimum,
        Event_Get_Orientation_Minimum(device));
    CreateIntSingle(device_data,
        "Orientation Maximum",
        &properties->orientation_maximum,
        Event_Get_Orientation_Maximum(device));

    // Log dump property. Will call Event_Dump_Debug_Log when its value is being
    // set.
    GesturesProp* dump_debug_log_prop = CreateBoolSingle(
        device_data, "Dump Debug Log", &properties->dump_debug_log, false);
    RegisterHandlers(device_data, dump_debug_log_prop, device, NULL,
        DumpTouchEvdevDebugLog);

    // Whether to do the gesture recognition or just passing the multi-touch data
    // to upper layers.
    CreateBoolSingle(device_data,
        "Raw Touch Passthrough",
        &properties->raw_passthrough,
        false);
    return true;
}

void GesturesPropFunctionsWrapper::UnregisterDevice(void* device_data)
{
    GesturePropertyProvider* provider = GetPropertyProvider(device_data);
    provider->UnregisterDevice(GetDeviceId(device_data));
}

template <typename T, class PROPTYPE>
GesturesProp* GesturesPropFunctionsWrapper::CreateProperty(void* device_data,
    const char* name,
    T* value,
    size_t count,
    const T* init)
{
    // Create the property. Use the default property value if possible.
    GesturesProp* default_property = NULL;
    if (!PreCreateProperty(device_data, name, &default_property))
        return NULL;
    GesturesProp* property = new PROPTYPE(name, count, value, init, default_property);

    // Start tracking the property in the provider.
    PostCreateProperty(device_data, name, make_scoped_ptr(property));
    return property;
}

bool GesturesPropFunctionsWrapper::PreCreateProperty(
    void* device_data,
    const char* name,
    GesturesProp** default_property)
{
    GesturePropertyProvider* provider = GetPropertyProvider(device_data);
    GesturePropertyProvider::DeviceId device_id = GetDeviceId(device_data);

    // Register the device in the property provider if not yet.
    provider->RegisterDevice(device_id, GetDevicePointer(device_data));

    // First, see if the GesturesProp already exists.
    DVLOG(3) << "Creating Property: \"" << name << "\"";
    GesturesProp* property = provider->FindProperty(device_id, name);

    // If so, delete it as we can't reuse the data structure (newly-created
    // property may have different data type and count, which are fixed upon
    // creation via the template mechanism).
    if (property) {
        LOG(WARNING) << "Gesture property \"" << name
                     << "\" re-created. This shouldn't happen at the normal usage.";
        Free(device_data, property);
    }

    // Return the found default property from conf files (could be NULL).
    *default_property = provider->GetDefaultProperty(device_id, name);
    return true;
}

void GesturesPropFunctionsWrapper::PostCreateProperty(
    void* device_data,
    const char* name,
    scoped_ptr<GesturesProp> property)
{
    // Log the creation.
    DVLOG(3) << "Created active prop: " << *property;

    // Add the property to the gesture property provider. The gesture property
    // provider will own it from now on.
    GesturePropertyProvider* provider = GetPropertyProvider(device_data);
    provider->AddProperty(GetDeviceId(device_data), name, std::move(property));
}

GesturesProp* GesturesPropFunctionsWrapper::CreateIntSingle(void* device_data,
    const char* name,
    int* value,
    int init)
{
    return CreateInt(device_data, name, value, 1, &init);
}

GesturesProp* GesturesPropFunctionsWrapper::CreateBoolSingle(
    void* device_data,
    const char* name,
    GesturesPropBool* value,
    GesturesPropBool init)
{
    return CreateBool(device_data, name, value, 1, &init);
}

GesturePropertyProvider* GesturesPropFunctionsWrapper::GetPropertyProvider(
    void* device_data)
{
    return static_cast<GestureInterpreterLibevdevCros*>(device_data)
        ->property_provider();
}

GesturePropertyProvider::DevicePtr
GesturesPropFunctionsWrapper::GetDevicePointer(void* device_data)
{
    return static_cast<GestureInterpreterLibevdevCros*>(device_data)->evdev();
}

GesturePropertyProvider::DeviceId GesturesPropFunctionsWrapper::GetDeviceId(
    void* device_data)
{
    return static_cast<GestureInterpreterLibevdevCros*>(device_data)->id();
}

/* Global GesturesPropProvider
 *
 * Used by PropRegistry in GestureInterpreter to forward property value
 * creations from there.
 * */
const GesturesPropProvider kGesturePropProvider = {
    GesturesPropFunctionsWrapper::CreateInt,
    GesturesPropFunctionsWrapper::CreateShort,
    GesturesPropFunctionsWrapper::CreateBool,
    GesturesPropFunctionsWrapper::CreateString,
    GesturesPropFunctionsWrapper::CreateReal,
    GesturesPropFunctionsWrapper::RegisterHandlers,
    GesturesPropFunctionsWrapper::Free
};

} // namespace ui
